/**
2694. 事件发射器 【订阅发布模式】
设计一个 EventEmitter 类。这个接口与 Node.js 或 DOM 的 Event Target 接口相似，但有一些差异。EventEmitter 应该允许订阅事件和触发事件。
你的 EventEmitter 类应该有以下两个方法：
subscribe - 这个方法接收两个参数：
一个作为字符串的事件名和一个回调函数。
当事件被触发时，这个回调函数将被调用。 一个事件应该能够有多个监听器。
当触发带有多个回调函数的事件时，应按照订阅的顺序依次调用每个回调函数。应返回一个结果数组。
你可以假设传递给 subscribe 的回调函数都不是引用相同的。 subscribe 方法还应返回一个对象，其中包含一个 unsubscribe 方法，使用户可以取消订阅。
当调用 unsubscribe 方法时，回调函数应该从订阅列表中删除，并返回 undefined。
emit - 这个方法接收两个参数：
一个作为字符串的事件名和一个可选的参数数组，这些参数将传递给回调函数。
如果没有订阅给定事件的回调函数，则返回一个空数组。否则，按照它们被订阅的顺序返回所有回调函数调用的结果数组。
输入：
actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"], 
values = [[], ["firstEvent", "function cb1() { return 5; }"],  ["firstEvent", "function cb1() { return 5; }"], ["firstEvent"]]
输出：[[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]
解释：
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], 还没有订阅任何回调函数
emitter.subscribe("firstEvent", function cb1() { return 5; });
emitter.subscribe("firstEvent", function cb2() { return 6; });
emitter.emit("firstEvent"); // [5, 6], 返回 cb1 和 cb2 的输出
 */
class EventEmitter {
  constructor() {
    this.events = {};
  }
  subscribe(eventName, callback) {
    this.events[eventName] ??= [];
    this.events[eventName].push(callback);
    return {
      unsubscribe: () => {
        this.events[eventName] = this.events[eventName].filter(i => i!==callback);
        if(this.events[eventName].length === 0) {
          delete this.events[eventName]
        }
      },
    };
  }

  emit(eventName, args = []) {
    if(!this.events[eventName]) {
      return [];
    }
    return this.events[eventName].map(i => i(...args));
  }
}